#include "tao/Environment.h"
#include "tao/ORB_Core.h"
#include "tao/SystemException.h"
#include "tao/default_environment.h"

#include "ace/OS_NS_string.h"

#if !defined (__ACE_INLINE__)
# include "tao/Environment.inl"
#endif /* __ACE_INLINE__ */

TAO_BEGIN_VERSIONED_NAMESPACE_DECL

CORBA::Environment::Environment (void)
  : exception_ (0)
  , previous_ (0)
{
}

CORBA::Environment::Environment (const CORBA::Environment& rhs)
  : exception_ (0)
  , previous_ (0)
{
  if (rhs.exception_)
    this->exception_ = rhs.exception_->_tao_duplicate ();
}

CORBA::Environment::Environment (TAO_ORB_Core* orb_core)
  : exception_ (0)
  , previous_ (orb_core->default_environment ())
{
  orb_core->default_environment (this);
}

CORBA::Environment&
CORBA::Environment::operator= (const CORBA::Environment& rhs)
{
  CORBA::Environment tmp (rhs);
  {
    CORBA::Exception *tmp_ex = this->exception_;
    this->exception_ = tmp.exception_;
    tmp.exception_ = tmp_ex;
  }
  {
    CORBA::Environment *tmp_env = this->previous_;
    this->previous_ = rhs.previous_;
    tmp.previous_ = tmp_env;
  }
  return *this;
}

CORBA::Environment::~Environment (void)
{
  this->clear ();

  // If previous is 0 then this is the first Environment, allocated
  // with the ORB, it shouldn't try to pop because the ORB is being
  // destroyed also.
  if (this->previous_ != 0)
    TAO_ORB_Core_instance ()->default_environment (this->previous_);
}

void
CORBA::Environment::exception (CORBA::Exception *ex)
{
  // @@ This does not look right, setting the exception to the
  //    contained exception is a bug,  the application is only
  //    supposed to pass in a pointer to an exception that it (the
  //    application) owns, however, if we contain the exception then
  //    *WE* own it.
  //    Normally I (coryan) would remove code like this, but I feel
  //    that it is a typical example of defensive programming for the
  //    *BAD*, i.e. we are not helping the application to get better
  //    and only making the ORB bigger and slower.
#if 0
  if (ex != this->exception_)
    {
      this->clear ();
    }
#else
  ACE_ASSERT (ex != this->exception_);
  this->clear ();
#endif /* 0 */

  this->exception_ = ex;

  if (this->exception_ != 0)
    this->exception_->_raise ();
}

void
CORBA::Environment::clear (void)
{
  delete this->exception_;
  this->exception_ = 0;
}

CORBA::Environment&
CORBA::Environment::default_environment ()
{
  // If we are using native C++ exceptions the user is *not* supposed
  // to clear the environment every time she calls into TAO.  In fact
  // the user is not supposed to use the environment at all!
  //
  // But TAO is using the default environment internally, thus
  // somebody has to clear it. Since TAO passes the environment around
  // this function should only be called when going from the user code
  // into TAO's code.
  //
  // This is not an issue when using the alternative C++ mapping (with
  // the Environment argument) because then the user is supposed to
  // clear the environment before calling into the ORB.
  TAO_ORB_Core_instance ()->default_environment ()->clear ();

  return TAO_default_environment ();;
}

// Convenience -- say if the exception is a system exception or not.

int
CORBA::Environment::exception_type (void) const
{
  // @@ Carlos, is this stuff that's properly "transformed" for EBCDIC
  //    platforms?!
  // @@ Doug: Yes, they are used to compare against the _id() of the
  //    exception, which should have been mappend to the native
  //    codeset.  Notice the "should" we haven't tried that stuff yet,
  //    and i find it hard to keep track of all the transformations
  //    going on, specially for the TypeCodes that are generated by
  //    the IDL compiler vs. the ones hard-coded in
  //    $TAO_ROOT/tao/Typecode_Constants.cpp

  static char sysex_prefix [] = "IDL:omg.org/CORBA/";
  static char typecode_extra [] = "TypeCode/";

  if (!this->exception_)
    {
      return CORBA::NO_EXCEPTION;
    }

  // All exceptions currently (CORBA 2.0) defined in the CORBA scope
  // are system exceptions ... except for a couple that are related to
  // TypeCodes.

  const char *id = this->exception_->_rep_id ();

  if ((ACE_OS::strncmp (id,
                        sysex_prefix,
                        sizeof sysex_prefix - 1) == 0
       && ACE_OS::strncmp (id + sizeof sysex_prefix - 1,
                           typecode_extra,
                           sizeof typecode_extra - 1) != 0))
    return CORBA::SYSTEM_EXCEPTION;
  else
    return CORBA::USER_EXCEPTION;
}

const char*
CORBA::Environment::exception_id (void) const
{
  if (this->exception_ == 0)
    return 0;

  return this->exception_->_rep_id ();
}

// Diagnostic utility routine: describe the exception onto the
// standard I/O stream passed as a parameter.

void
CORBA::Environment::print_exception (const char *info,
                                     FILE *) const
{
  if (this->exception_)
    {
      const char *id = this->exception_->_rep_id ();

      TAOLIB_ERROR ((LM_ERROR,
                  ACE_TEXT ("TAO: (%P|%t) EXCEPTION, %C\n"),
                  info));

      CORBA::SystemException *x2 =
        CORBA::SystemException::_downcast (this->exception_);

      if (x2 != 0)
        x2->_tao_print_system_exception ();
      else
        // @@ we can use the exception's typecode to dump all the data
        // held within it ...

        TAOLIB_ERROR ((LM_ERROR,
                    ACE_TEXT ("TAO: (%P|%t) user exception, ID '%C'\n"),
                    id));
    }
  else
    TAOLIB_ERROR ((LM_ERROR,
                ACE_TEXT ("TAO: (%P|%t) no exception, %C\n"), info));
}

TAO_END_VERSIONED_NAMESPACE_DECL
